package xiaoa.java.utils;

import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;

public class DownUtils {

	/** 下载文件地址 */
	private String downloadFilePath;

	/** 下载文件保存位置 */
	private String saveFilePath;

	/** 使用线程数 */
	private int threadNum;

	/** 下载文件总大小 */
	private long downloadSize;

	/** 下载线程 */
	private DownloadThread[] downloadThreads;

	/**
	 * 构造方法
	 * 
	 * @param downloadFilePath
	 *            下载文件地址
	 * @param saveFilePath
	 *            下载文件保存位置
	 * @param threadNum
	 *            使用线程数
	 */
	public DownUtils(String downloadFilePath, String saveFilePath, int threadNum) {
		this.downloadFilePath = downloadFilePath;
		this.saveFilePath = saveFilePath;
		this.threadNum = threadNum;

		// 初始化下载的线程数
		downloadThreads = new DownloadThread[threadNum];
	}

	
	public long getSize(){
		// 统计所有线程下载的百分比
		long sumSize = 0;

		for (DownloadThread thread : downloadThreads) {
			sumSize += thread.getLength();
		}
		return sumSize;
		
	}
	
	/**
	 * 下载百分比
	 * 
	 * @return
	 */
	public double getCompleteRate() {
		long sumSize = 0;

		if (downloadSize > 0) {
			// 统计所有线程下载的百分比
			for (DownloadThread thread : downloadThreads) {
				sumSize += thread.getLength();
			}
			return sumSize * 1.0 / downloadSize;
		}
		return 0;
	}

	/**
	 * 下载文件
	 * 
	 * @throws Exception
	 *             下载过程中的异常
	 */
	public void download() throws Exception {
		// 获得下载文件大小
		downloadSize = getDownloadFileSize();
        System.out.println("文件大小"+downloadSize/1024/1024+"/M");
		// 计算每个线程下载的大小
		long threadPartSize = downloadSize / threadNum + 1;

		// 创建随机访问文件类创建目标文件位置
		RandomAccessFile saveFile = new RandomAccessFile(saveFilePath, "rw");

		// 设置目标文件大小，即下载文件的大小
		saveFile.setLength(downloadSize);

		// 关闭访问
		saveFile.close();

		// 启动指定线程数开始下载
		for (int i = 0; i < threadNum; i++) {
			// 计算每个线程开始下载的位置
			long startPosition = i * threadPartSize;


			//打印线程读取范围
            System.out.println("线程"+i+"开始位置"+startPosition+"结束位置"+(startPosition+threadPartSize));

            // 创建下载线程并启动下载
			downloadThreads[i] = new DownloadThread(startPosition, threadPartSize);
			downloadThreads[i].start();
		}
	}

	/**
	 * 下载线程
	 */
	private class DownloadThread extends Thread {
		// 下载位置
		private long startPosition;

		// 下载的大小
		private long threadPartSize;

		// 已经下载的字节大小
		private long length;

		/**
		 * 构造方法
		 * 
		 * @param startPosition
		 * @param threadPartSize
		 * @param threadFile
		 */
		public DownloadThread(long startPosition, long threadPartSize) {
			this.startPosition = startPosition;
			this.threadPartSize = threadPartSize;
		}

		/**
		 * 已经下载文件大小
		 * 
		 * @return
		 */
		public long getLength() {
			return this.length;
		}

		/**
		 * 线程运行的方法
		 */
		@Override
		public void run() {
			// 下载路径
			URL downloadUrl;
			
			
			// 每个线程都将下载数据写到目标文件中
			try (RandomAccessFile threadFile = new RandomAccessFile(saveFilePath, "rw");){

				// 指定该线程写入到目标文件的位置
				threadFile.seek(startPosition);
				
				downloadUrl = new URL(downloadFilePath);
				// 建立连接
				HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();

				/* 设置参数 */
				setRequestProerty(conn);

				// 建立真实连接
				conn.connect();

				// 获得下载流
				InputStream input = conn.getInputStream();

				// 该线程下载自己负责的部分，跳过startPosition字节
				input.skip(startPosition);

				// 定义缓冲区大小
				byte[] buffer = new byte[1024*100];
				int len = -1;
				// 读取数据并写入文件
				while (length < threadPartSize
						&& (len = input.read(buffer)) != -1) {
					threadFile.write(buffer, 0, len);
					// 累计下载的大小
					this.length += len;
					
				}

				// 关闭文件与流
				threadFile.close();
				input.close();
			} catch (Exception e) {
				e.printStackTrace();
				
				System.out.println("重试  ： " + startPosition);
				
				run();
			}

		}
	}

	/**
	 * 获得下载文件的大小
	 * 
	 * @return 下载文件的字节数
	 * @throws Exception
	 *             查询过程中的异常
	 */
	public long getDownloadFileSize() throws Exception {
		// 下载路径
		URL downloadUrl = new URL(downloadFilePath);

		// 建立连接
		HttpURLConnection conn = (HttpURLConnection) downloadUrl
				.openConnection();

		/* 设置参数 */
		setRequestProerty(conn);

		// 建立真实连接
		conn.connect();

		// 获得下载文件的大小
		long fileSize = conn.getContentLengthLong();

		// 关闭连接
		conn.disconnect();
		return fileSize;
	}

	/**
	 * 设置请求头信息
	 * 
	 * @param conn
	 *            连接对象
	 */
	private void setRequestProerty(HttpURLConnection conn)
			throws ProtocolException {
		// 设置请求方法
		conn.setRequestMethod("GET");

		// 设置请求头信息
		conn.setRequestProperty("Accept", "*/*");
		conn.setRequestProperty("Connection", "Keep-Alive");
		conn.setRequestProperty("User-Agent",
				"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
		conn.setRequestProperty("Accept-Language",
				"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
		conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
		conn.setRequestProperty("Charset", "UTF-8");

		// 设置连接超时时间
		conn.setConnectTimeout(5000);
	}

    public static void main(String[] args) throws Exception {
        String downloadFilePath = "http://114.80.155.174/bigota.d.miui.com/V8.5.6.0.NDFCNED/miui_UGGLITE_V8.5.6.0.NDFCNED_719fcb3e54_7.1.zip";
        final DownUtils downloadUtil = new DownUtils(downloadFilePath,
                "D:\\androidstudio_2.3.0.0.exe", 1);
        downloadUtil.download();
        //System.out.println("文件总大小：" + downloadUtil.getDownloadFileSize());
        // 定时任务调试查看完成进度
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
        	long lastSize = 0;
            @Override
            public void run() {
            	
            	long size =  downloadUtil.getSize();
            	
                // 获得完成情况
                double completeRate = downloadUtil.getCompleteRate();
                int downloadStauts = (int) (completeRate * 100);

                // 如果完成，取消任务
                if (downloadStauts >= 100) {
                    timer.cancel();
                }
                
                System.out.println(downloadStauts + "%   size = " + size / 1024 / 1024  + "M   increment = " + (size - lastSize ) / 1024  + "K"  );
                lastSize = size;
            }
        }, 0, 1000);
    }

}
